library(tidyverse)         # for graphing and data cleaning
library(tidymodels)        # for modeling
library(naniar)            # for analyzing missing values
library(vip)               # for variable importance plots
theme_set(theme_minimal()) # Lisa's favorite theme
hotels <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2020/2020-02-11/hotels.csv')

When you finish the assignment, remove the # from the options chunk at the top, so that messages and warnings aren’t printed. If you are getting errors in your code, add error = TRUE so that the file knits. I would recommend not removing the # until you are completely finished.

Setting up Git and GitHub in RStudio

Read the Quick Intro section of the Using git and GitHub in R Studio set of Course Materials. Set up Git and GitHub and create a GitHub repo and associated R Project (done for you when you clone the repo) for this homework assignment. Put this file into the project. You should always open the R Project (.Rproj) file when you work with any of the files in the project.

Task: Below, post a link to your GitHub repository. link

Creating a website

You’ll be using RStudio to create a personal website to showcase your work from this class! Start by watching the Sharing on Short Notice webinar by Alison Hill and Desirée De Leon of RStudio. This should help you choose the type of website you’d like to create.

Once you’ve chosen that, you might want to look through some of the other Building a website resources I posted on the resources page of our course website. I highly recommend making a nice landing page where you give a brief introduction of yourself.

Tasks:

  • Include a link to your website below. (If anyone does not want to post a website publicly, please talk to me and we will find a different solution).

my page

  • Listen to at least the first 20 minutes of “Building a Career in Data Science, Chapter 4: Building a Portfolio”. Go to the main podcast website and navigate to a podcast provider that works for you to find that specific episode. Write 2-3 sentences reflecting on what they discussed and why creating a website might be helpful for you.

Portfolios work as evidence for employers to see what my skills are. Basic capabilities for coding and just getting something out there is more important than having something perfect. Portfolios give me an incentive to learn a new technique. Porfolios also allow people to get involved in the community. In general, a good portfolio is something that other people can digest.

  • (Optional) Create an R package with your own customized gpplot2 theme! Write a post on your website about why you made the choices you did for the theme. See the Building an R package and Custom ggplot2 themes resources.

Machine Learning review and intro to tidymodels

Read through and follow along with the Machine Learning review with an intro to the tidymodels package posted on the Course Materials page.

Tasks:

  1. Read about the hotel booking data, hotels, on the Tidy Tuesday page it came from. There is also a link to an article from the original authors. The outcome we will be predicting is called is_canceled.
  • Without doing any analysis, what are some variables you think might be predictive and why?

Previous cancellations, previous bookings not canceled, reserved room type, deposit type, days in waitlist. Because customers that have cancelled before might be more prompt to cancelling. Similarly, people that book their spot but don’t cancel are less likely to cancel. The room type could affect cancelation, if for example the room is very expensive it might have required more thought and therefore the client might be less likely to cancel. Also, people that made a deposit are also elss likely to cancel. Lastly, people that waited for a longer time to get a room could be less prompt to cancel, this is the same case for the lead time variable.

_ What are some problems that might exist with the data? You might think about how it was collected and who did the collecting.

The data might not respect the customers given that they were not asked for consent, the data was gathered using software automatically therefore, there was no space for a dialogue between the sample population and the entity gathering the data.

  • If we construct a model, what type of conclusions will be able to draw from it?

We will be able to identify which variables are the most important when predicting what guests are more likely to cancel.

  1. Create some exploratory plots or table summaries of the data, concentrating most on relationships with the response variable. Keep in mind the response variable is numeric, 0 or 1. You may want to make it categorical (you also may not). Be sure to also examine missing values or other interesting values.

Quantitative Variables

hotels %>% 
  select(where(is.numeric)) %>% 
  pivot_longer(cols = everything(),
               names_to = "variable", 
               values_to = "value") %>% 
  ggplot(aes(x = value)) +
  geom_histogram() +
  facet_wrap(vars(variable), 
             scales = "free")

There are a lot of counts of 0 for different variables like children, bookings not canceled, previous cancelations, days in waiting list, is cancelled, days in waiting list, babies, adr, adults, and is repeated gest therefore I would need to create an indicator variable for each. Arrival day date week number has a normal distribution, lead time is right skewed and arrival date day of month has a pretty uniform distribution. Also, arrival date day of the month has too many unique levels, therefore, I won’t use it as a variable. .

hotels %>% 
  count(is_repeated_guest) 

We can see that only few guests are repeated guests.

hotels %>% 
  select(children, previous_bookings_not_canceled, booking_changes, required_car_parking_spaces, babies) %>%
  pivot_longer(cols = everything(),
               names_to = "variable", 
               values_to = "value") %>% 
  ggplot(aes(x = value)) +
  geom_histogram() +
  facet_wrap(vars(variable), 
             scales = "free")

I realised that the number of booking changes, the number of children and the number of previous bookings not canceled have levels with low counts therefore, I could create fewer levels for them.

Categorical variables:

hotels %>% 
  select(where(is.character)) %>% 
  pivot_longer(cols = everything(),
               names_to = "variable", 
               values_to = "value") 

None of the variables is already formatted as a factor. Many of them have a character type, this amount to 1,552,070 observations. I would have to transform those variables into factors to be able to use the information.

hotels %>% 
  count(deposit = deposit_type) %>% 
  ggplot(aes(x = deposit, y = n)) +
  geom_col()

I think that the deposit type could be a good predictor for whether or not people cancel their reservation.

hotels %>% 
  add_n_miss() %>% 
  count(n_miss_all)

There are 4 missing values.

  1. First, we will do a couple things to get the data ready, including making the outcome a factor (needs to be that way for logistic regression), removing the year variable and some reservation status variables, and removing missing values (not NULLs but true missing values). Split the data into a training and test set, stratifying on the outcome variable, is_canceled. Since we have a lot of data, we’re going to split the data 50/50 between training and test. I have already set.seed() for you. Be sure to use hotels_mod in the splitting.
hotels_mod <- hotels %>% 
  mutate(is_canceled = as.factor(is_canceled)) %>% 
  mutate(across(where(is.character), as.factor)) %>% 
  select(-arrival_date_year,
         -reservation_status,
         -reservation_status_date) %>% 
  add_n_miss() %>% 
  filter(n_miss_all == 0) %>% 
  select(-n_miss_all)

set.seed(494)

hotels_split <- initial_split(hotels_mod, 
                              strata = is_canceled,
                              prop = .5)
hotels_split
## <Analysis/Assess/Total>
## <59693/59693/119386>
#<training/testing/total>

hotels_training <- training(hotels_split)
hotels_testing <- testing(hotels_split)
  1. In this next step, we are going to do the pre-processing. Usually, I won’t tell you exactly what to do here, but for your first exercise, I’ll tell you the steps.
  • Set up the recipe with is_canceled as the outcome and all other variables as predictors (HINT: ~.).
  • Use a step_XXX() function or functions (I think there are other ways to do this, but I found step_mutate_at() easiest) to create some indicator variables for the following variables: children, babies, and previous_cancellations. So, the new variable should be a 1 if the original is more than 0 and 0 otherwise. Make sure you do this in a way that accounts for values that may be larger than any we see in the dataset.
  • For the agent and company variables, make new indicator variables that are 1 if they have a value of NULL and 0 otherwise.
  • Use fct_lump_n() to lump together countries that aren’t in the top 5 most occurring.
  • If you used new names for some of the new variables you created, then remove any variables that are no longer needed.
  • Use step_normalize() to center and scale all the non-categorical predictor variables. (Do this BEFORE creating dummy variables. When I tried to do it after, I ran into an error - I’m still investigating why.)
  • Create dummy variables for all factors/categorical predictor variables (make sure you have -all_outcomes() in this part!!).
  • Use the prep() and juice() functions to apply the steps to the training data just to check that everything went as planned.
hotels_recipe <- recipe(is_canceled ~ .,
                       data = hotels_training) %>% 
  step_mutate_at(children,babies,previous_cancellations, fn = ~as.numeric(.>0 )) %>% 
  step_mutate_at(agent,company, fn = ~as.numeric(.=="NULL" )) %>% 
  step_mutate_at(country, fn = ~ fct_lump_n(country, n = 5)) %>% 
  step_normalize(all_predictors(), 
                 -all_nominal()) %>% 
  step_dummy(all_nominal(),
             -all_outcomes()) 
hotels_recipe %>% 
  prep(hotels_training) %>%
  juice() 
  1. In this step we will set up a LASSO model and workflow.
  • In general, why would we want to use LASSO instead of regular logistic regression? (HINT: think about what happens to the coefficients).

Because we want to identify coefficients that minimize a penalized version of the sum of squared errors. We want to penalize variables that are non-informative and shrinks them towards 0.

  • Define the model type, set the engine, set the penalty argument to tune() as a placeholder, and set the mode.
hotels_lasso_mod <- 
  logistic_reg(mixture = 1) %>% 
  set_engine("glmnet") %>% 
  set_args(penalty = tune()) %>% 
  set_mode("classification")
  • Create a workflow with the recipe and model.
hotels_lasso_wf <- 
  workflow() %>% 
  add_recipe(hotels_recipe) %>% 
  add_model(hotels_lasso_mod)

hotels_lasso_wf
## ══ Workflow ════════════════════════════════════════════════════════════════════
## Preprocessor: Recipe
## Model: logistic_reg()
## 
## ── Preprocessor ────────────────────────────────────────────────────────────────
## 5 Recipe Steps
## 
## ● step_mutate_at()
## ● step_mutate_at()
## ● step_mutate_at()
## ● step_normalize()
## ● step_dummy()
## 
## ── Model ───────────────────────────────────────────────────────────────────────
## Logistic Regression Model Specification (classification)
## 
## Main Arguments:
##   penalty = tune()
##   mixture = 1
## 
## Computational engine: glmnet
  1. In this step, we’ll tune the model and fit the model using the best tuning parameter to the entire training dataset.
  • Create a 5-fold cross-validation sample. We’ll use this later. I have set the seed for you.
  • Use the grid_regular() function to create a grid of 10 potential penalty parameters (we’re keeping this sort of small because the dataset is pretty large). Use that with the 5-fold cv data to tune the model.
  • Use the tune_grid() function to fit the models with different tuning parameters to the different cross-validation sets.
  • Use the collect_metrics() function to collect all the metrics from the previous step and create a plot with the accuracy on the y-axis and the penalty term on the x-axis. Put the x-axis on the log scale.
  • Use the select_best() function to find the best tuning parameter, fit the model using that tuning parameter to the entire training set (HINT: finalize_workflow() and fit()), and display the model results using pull_workflow_fit() and tidy(). Are there some variables with coefficients of 0?
set.seed(494) # for reproducibility
hotels_cv <- vfold_cv(hotels_training, v = 5)
penalty_grid <- grid_regular(penalty(),
                             levels = 10)
hotels_lasso_tune <- 
  hotels_lasso_wf %>% 
  tune_grid(
    resamples = hotels_cv,
    grid = penalty_grid
    )
hotels_lasso_tune
hotels_lasso_tune %>% 
  collect_metrics()
hotels_lasso_tune %>% 
  collect_metrics() %>% 
  filter(.metric == "accuracy") %>% 
  ggplot(aes(x = penalty, y = mean)) +
  geom_point() +
  geom_line() +
  scale_x_log10(
   breaks = scales::trans_breaks("log10", function(x) 10^x),
   labels = scales::trans_format("log10",scales::math_format(10^.x))) +
  labs(x = "penalty", y = "accuracy")

hotels_lasso_tune %>% 
  show_best(metric = "accuracy")
best_param <- hotels_lasso_tune %>% 
  select_best(metric = "accuracy")
best_param
hotels_lasso_final_wf <- hotels_lasso_wf %>% 
  finalize_workflow(best_param)
hotels_lasso_final_wf
## ══ Workflow ════════════════════════════════════════════════════════════════════
## Preprocessor: Recipe
## Model: logistic_reg()
## 
## ── Preprocessor ────────────────────────────────────────────────────────────────
## 5 Recipe Steps
## 
## ● step_mutate_at()
## ● step_mutate_at()
## ● step_mutate_at()
## ● step_normalize()
## ● step_dummy()
## 
## ── Model ───────────────────────────────────────────────────────────────────────
## Logistic Regression Model Specification (classification)
## 
## Main Arguments:
##   penalty = 0.000464158883361278
##   mixture = 1
## 
## Computational engine: glmnet
hotels_lasso_final_mod <- hotels_lasso_final_wf %>% 
  fit(data = hotels_training)

hotels_lasso_final_mod %>% 
  pull_workflow_fit() %>% 
  tidy() 

The variables with a 0 coefficient are babies, arrival_date_month_October, market_segment_Corporate, market_segment_Undefined, distribution_channel_Undefined,assigned_room_type_P.

  1. Now that we have a model, let’s evaluate it a bit more. All we have looked at so far is the cross-validated accuracy from the previous step.
  • Create a variable importance graph. Which variables show up as the most important? Are you surprised?
hotels_lasso_final_mod %>% 
  pull_workflow_fit() %>% 
  vip()

The most important seem to be reserved room type P, assigned room type I, and deposit type Non.Refund. I am not surprised, I think that reserving a certain type of room could make guests more prompt to cancel the reservation easily relative to other types. Also, being assigned to a room type that the guests don’t like sounds like a reasonable preductor for cancelling. Lastly, as we previously mentioned, the refundability of the deposit could affect the decision of a guest to cancel their booking.

  • Use the last_fit() function to fit the final model and then apply it to the testing data. Report the metrics from the testing data using the collet_metrics() function. How do they compare to the cross-validated metrics?
hotels_lasso_test <- hotels_lasso_final_wf %>% 
  last_fit(hotels_split)

hotels_lasso_test %>% 
  collect_metrics()
hotels_lasso_tune %>% 
  collect_metrics()

They are very similar for both accuracy and roc_auc metrics.

  • Use the collect_predictions() function to find the predicted probabilities and classes for the test data. Save this to a new dataset called preds. Then, use the conf_mat() function from dials (part of tidymodels) to create a confusion matrix showing the predicted classes vs. the true classes. What is the true positive rate (sensitivity)? What is the true negative rate (specificity)? See this Wikipedia reference if you (like me) tend to forget these definitions.
preds <- 
  collect_predictions(hotels_lasso_test)
preds %>% 
  conf_mat(is_canceled,.pred_class)
##           Truth
## Prediction     0     1
##          0 34372  7839
##          1  3211 14271
#Sensitivity:
34372/(34372+3211)
## [1] 0.9145624
#Specificity:
14271/(7839+14271)
## [1] 0.6454545

Sensitivity is 0.9145624 and Specificity is 0.6454545.

  • Use the preds dataset you just created to create a density plot of the predicted probabilities of canceling (the variable is called .pred_1), filling by is_canceled. Use an alpha = .5 and color = NA in the geom_density(). Answer these questions: a. What would this graph look like for a model with an accuracy that was close to 1? b. Our predictions are classified as canceled if their predicted probability of canceling is greater than .5. If we wanted to have a high true positive rate, should we make the cutoff for predicted as canceled higher or lower than .5? c. What happens to the true negative rate if we try to get a higher true positive rate?
preds %>% 
  ggplot(aes(x = .pred_1, fill = is_canceled)) +
  geom_density(alpha = .5, 
             color = NA) +
  labs(x = "Probabilities", 
       y = "Density",
       title =  "Predicted probabilities of canceling")

  1. The graph would have densities clustered on the extremes considering that we would be able to predict with more certainty whether or not the guest cancelled. b.Lower than 0.5, since a lower cut-off promotes a higher sensitivity and lower specificity. c.The true negative would then be lower.
  1. Let’s say that this model is going to be applied to bookings 14 days in advance of their arrival at each hotel, and someone who works for the hotel will make a phone call to the person who made the booking. During this phone call, they will try to assure that the person will be keeping their reservation or that they will be canceling in which case they can do that now and still have time to fill the room. How should the hotel go about deciding who to call? How could they measure whether it was worth the effort to do the calling? Can you think of another way they might use the model?

They should call the clients that booked a room of the type that we identified as variables of importance for the prediction of whether they cancel or not. Also they should call the clients that have a non-refundable deposit. They could measure the value of this work by comparing the proportion of cancellations from one year to the other and the associated monetary effects of the changes. They could use this model to better advertise or make more appealing the rooms that were considered as relevant predictors for cancelation.

  1. How might you go about questioning and evaluating the model in terms of fairness? Are there any questions you would like to ask of the people who collected the data?

I would question the geographic distributions of the locations of the hotels that could define specific trends, preferences or in the case of our model, define a specific country of origin as relevant . I would like to ask whether or not they let the guests know the data was being collected and also, how they persuaded hotels’ administration to allow them to gather the data. Also, I would like to ask them what was the purpose of this work.

Bias and Fairness

Listen to Dr. Rachel Thomas’s Bias and Fairness lecture. Write a brief paragraph reflecting on it. You might also be interested in reading the ProPublica article Dr. Thomas references about using a tool called COMPAS to predict recidivism. Some questions/ideas you might keep in mind:

  • Did you hear anything that surprised you?
  • Why is it important that we pay attention to bias and fairness when studying data science?
  • Is there a type of bias Dr. Thomas discussed that was new to you? Can you think about places you have seen these types of biases?

I was surprised by how blinding can be the use of machines and algorythms when dealing with socially relevant issues. Feed-back loops can bias our models providing findings that are self-fulfilling that might seem fair. I was also surprised that Historical bias couldn’t be mitigated as it is a structural problem. Therefore, structural social problems can potentially always permeate in any work of social relevance. I have seen these problems arise in political conversations, when people quote different studies and data sources that seem legitimate, but don’t question whether or not there were any biases on their sampling. Not talking to domain experts and those impacted can lead to problematic conclusions. A similar consequence can be Fairwashing, which is making a fair explanation to justify unfair findings. Lastly, one quote that I also liked is that sometimes the answer is not to build. Data Scientists have great power and credibility, given machine imperfections, if this power is not used responsibly it can harm many people.

LS0tCnRpdGxlOiAnQXNzaWdubWVudCAjMScKYXV0aG9yOiAnRnJhbmNvIFNhbGluYXMnCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBkZl9wcmludDogcGFnZWQKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFKQpgYGAKCmBgYHtyIGxpYnJhcmllc30KbGlicmFyeSh0aWR5dmVyc2UpICAgICAgICAgIyBmb3IgZ3JhcGhpbmcgYW5kIGRhdGEgY2xlYW5pbmcKbGlicmFyeSh0aWR5bW9kZWxzKSAgICAgICAgIyBmb3IgbW9kZWxpbmcKbGlicmFyeShuYW5pYXIpICAgICAgICAgICAgIyBmb3IgYW5hbHl6aW5nIG1pc3NpbmcgdmFsdWVzCmxpYnJhcnkodmlwKSAgICAgICAgICAgICAgICMgZm9yIHZhcmlhYmxlIGltcG9ydGFuY2UgcGxvdHMKdGhlbWVfc2V0KHRoZW1lX21pbmltYWwoKSkgIyBMaXNhJ3MgZmF2b3JpdGUgdGhlbWUKYGBgCgpgYGB7ciBkYXRhfQpob3RlbHMgPC0gcmVhZHI6OnJlYWRfY3N2KCdodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5L21hc3Rlci9kYXRhLzIwMjAvMjAyMC0wMi0xMS9ob3RlbHMuY3N2JykKYGBgCgoKV2hlbiB5b3UgZmluaXNoIHRoZSBhc3NpZ25tZW50LCByZW1vdmUgdGhlIGAjYCBmcm9tIHRoZSBvcHRpb25zIGNodW5rIGF0IHRoZSB0b3AsIHNvIHRoYXQgbWVzc2FnZXMgYW5kIHdhcm5pbmdzIGFyZW4ndCBwcmludGVkLiBJZiB5b3UgYXJlIGdldHRpbmcgZXJyb3JzIGluIHlvdXIgY29kZSwgYWRkIGBlcnJvciA9IFRSVUVgIHNvIHRoYXQgdGhlIGZpbGUga25pdHMuIEkgd291bGQgcmVjb21tZW5kIG5vdCByZW1vdmluZyB0aGUgYCNgIHVudGlsIHlvdSBhcmUgY29tcGxldGVseSBmaW5pc2hlZC4KCiMjIFNldHRpbmcgdXAgR2l0IGFuZCBHaXRIdWIgaW4gUlN0dWRpbwoKUmVhZCB0aGUgW1F1aWNrIEludHJvXShodHRwczovL2FkdmFuY2VkLWRzLWluLXIubmV0bGlmeS5hcHAvcG9zdHMvMjAyMS0wMS0yOC1naXRnaXRodWIvI3F1aWNrLWludHJvKSBzZWN0aW9uIG9mIHRoZSBVc2luZyBnaXQgYW5kIEdpdEh1YiBpbiBSIFN0dWRpbyBzZXQgb2YgQ291cnNlIE1hdGVyaWFscy4gU2V0IHVwIEdpdCBhbmQgR2l0SHViIGFuZCBjcmVhdGUgYSBHaXRIdWIgcmVwbyBhbmQgYXNzb2NpYXRlZCBSIFByb2plY3QgKGRvbmUgZm9yIHlvdSB3aGVuIHlvdSBjbG9uZSB0aGUgcmVwbykgZm9yIHRoaXMgaG9tZXdvcmsgYXNzaWdubWVudC4gUHV0IHRoaXMgZmlsZSBpbnRvIHRoZSBwcm9qZWN0LiBZb3Ugc2hvdWxkIGFsd2F5cyBvcGVuIHRoZSBSIFByb2plY3QgKC5ScHJvaikgZmlsZSB3aGVuIHlvdSB3b3JrIHdpdGggYW55IG9mIHRoZSBmaWxlcyBpbiB0aGUgcHJvamVjdC4gCgoqKlRhc2sqKjogQmVsb3csIHBvc3QgYSBsaW5rIHRvIHlvdXIgR2l0SHViIHJlcG9zaXRvcnkuCltsaW5rXShodHRwczovL2dpdGh1Yi5jb20vZnJhbmNvczE5OTgvZnJhbmNvc2FsaW5hc19kaXN0aWxsKQoKCiMjIENyZWF0aW5nIGEgd2Vic2l0ZQoKWW91J2xsIGJlIHVzaW5nIFJTdHVkaW8gdG8gY3JlYXRlIGEgcGVyc29uYWwgd2Vic2l0ZSB0byBzaG93Y2FzZSB5b3VyIHdvcmsgZnJvbSB0aGlzIGNsYXNzISBTdGFydCBieSB3YXRjaGluZyB0aGUgW1NoYXJpbmcgb24gU2hvcnQgTm90aWNlXShodHRwczovL3JzdHVkaW8uY29tL3Jlc291cmNlcy93ZWJpbmFycy9zaGFyaW5nLW9uLXNob3J0LW5vdGljZS1ob3ctdG8tZ2V0LXlvdXItbWF0ZXJpYWxzLW9ubGluZS13aXRoLXItbWFya2Rvd24vKSB3ZWJpbmFyIGJ5IEFsaXNvbiBIaWxsIGFuZCBEZXNpcsOpZSBEZSBMZW9uIG9mIFJTdHVkaW8uIFRoaXMgc2hvdWxkIGhlbHAgeW91IGNob29zZSB0aGUgdHlwZSBvZiB3ZWJzaXRlIHlvdSdkIGxpa2UgdG8gY3JlYXRlLiAKCk9uY2UgeW91J3ZlIGNob3NlbiB0aGF0LCB5b3UgbWlnaHQgd2FudCB0byBsb29rIHRocm91Z2ggc29tZSBvZiB0aGUgb3RoZXIgKkJ1aWxkaW5nIGEgd2Vic2l0ZSogcmVzb3VyY2VzIEkgcG9zdGVkIG9uIHRoZSBbcmVzb3VyY2VzIHBhZ2VdKGh0dHBzOi8vYWR2YW5jZWQtZHMtaW4tci5uZXRsaWZ5LmFwcC9yZXNvdXJjZXMuaHRtbCkgb2Ygb3VyIGNvdXJzZSB3ZWJzaXRlLiBJIGhpZ2hseSByZWNvbW1lbmQgbWFraW5nIGEgbmljZSBsYW5kaW5nIHBhZ2Ugd2hlcmUgeW91IGdpdmUgYSBicmllZiBpbnRyb2R1Y3Rpb24gb2YgeW91cnNlbGYuIAoKCioqVGFza3MqKjoKCiogSW5jbHVkZSBhIGxpbmsgdG8geW91ciB3ZWJzaXRlIGJlbG93LiAoSWYgYW55b25lIGRvZXMgbm90IHdhbnQgdG8gcG9zdCBhIHdlYnNpdGUgcHVibGljbHksIHBsZWFzZSB0YWxrIHRvIG1lIGFuZCB3ZSB3aWxsIGZpbmQgYSBkaWZmZXJlbnQgc29sdXRpb24pLiAgCgpbbXkgcGFnZV0oaHR0cHM6Ly9mcmFuY29zYWxpbmFzLm5ldGxpZnkuYXBwKQoKKiBMaXN0ZW4gdG8gYXQgbGVhc3QgdGhlIGZpcnN0IDIwIG1pbnV0ZXMgb2YgIkJ1aWxkaW5nIGEgQ2FyZWVyIGluIERhdGEgU2NpZW5jZSwgQ2hhcHRlciA0OiBCdWlsZGluZyBhIFBvcnRmb2xpbyIuIEdvIHRvIHRoZSBtYWluIFtwb2RjYXN0IHdlYnNpdGVdKGh0dHBzOi8vcG9kY2FzdC5iZXN0Ym9vay5jb29sLykgYW5kIG5hdmlnYXRlIHRvIGEgcG9kY2FzdCBwcm92aWRlciB0aGF0IHdvcmtzIGZvciB5b3UgdG8gZmluZCB0aGF0IHNwZWNpZmljIGVwaXNvZGUuIFdyaXRlIDItMyBzZW50ZW5jZXMgcmVmbGVjdGluZyBvbiB3aGF0IHRoZXkgZGlzY3Vzc2VkIGFuZCB3aHkgY3JlYXRpbmcgYSB3ZWJzaXRlIG1pZ2h0IGJlIGhlbHBmdWwgZm9yIHlvdS4gCgoKPlBvcnRmb2xpb3Mgd29yayBhcyBldmlkZW5jZSBmb3IgZW1wbG95ZXJzIHRvIHNlZSB3aGF0IG15IHNraWxscyBhcmUuIEJhc2ljIGNhcGFiaWxpdGllcyBmb3IgY29kaW5nIGFuZCBqdXN0IGdldHRpbmcgc29tZXRoaW5nIG91dCB0aGVyZSBpcyBtb3JlIGltcG9ydGFudCB0aGFuIGhhdmluZyBzb21ldGhpbmcgcGVyZmVjdC4gUG9ydGZvbGlvcyBnaXZlIG1lIGFuIGluY2VudGl2ZSB0byBsZWFybiBhIG5ldyB0ZWNobmlxdWUuIFBvcmZvbGlvcyBhbHNvIGFsbG93IHBlb3BsZSB0byBnZXQgaW52b2x2ZWQgaW4gdGhlIGNvbW11bml0eS4gSW4gZ2VuZXJhbCwgYSBnb29kIHBvcnRmb2xpbyBpcyBzb21ldGhpbmcgdGhhdCBvdGhlciBwZW9wbGUgY2FuIGRpZ2VzdC4gCgoKKiAoT3B0aW9uYWwpIENyZWF0ZSBhbiBSIHBhY2thZ2Ugd2l0aCB5b3VyIG93biBjdXN0b21pemVkIGBncHBsb3QyYCB0aGVtZSEgV3JpdGUgYSBwb3N0IG9uIHlvdXIgd2Vic2l0ZSBhYm91dCB3aHkgeW91IG1hZGUgdGhlIGNob2ljZXMgeW91IGRpZCBmb3IgdGhlIHRoZW1lLiBTZWUgdGhlICpCdWlsZGluZyBhbiBSIHBhY2thZ2UqIGFuZCAqQ3VzdG9tIGBnZ3Bsb3QyYCB0aGVtZXMqIFtyZXNvdXJjZXNdKGh0dHBzOi8vYWR2YW5jZWQtZHMtaW4tci5uZXRsaWZ5LmFwcC9yZXNvdXJjZXMuaHRtbCkuIAoKIyMgTWFjaGluZSBMZWFybmluZyByZXZpZXcgYW5kIGludHJvIHRvIGB0aWR5bW9kZWxzYAoKUmVhZCB0aHJvdWdoIGFuZCBmb2xsb3cgYWxvbmcgd2l0aCB0aGUgW01hY2hpbmUgTGVhcm5pbmcgcmV2aWV3IHdpdGggYW4gaW50cm8gdG8gdGhlIGB0aWR5bW9kZWxzYCBwYWNrYWdlXShodHRwczovL2FkdmFuY2VkLWRzLWluLXIubmV0bGlmeS5hcHAvcG9zdHMvMjAyMS0wMy0xNi1tbC1yZXZpZXcvKSBwb3N0ZWQgb24gdGhlIENvdXJzZSBNYXRlcmlhbHMgcGFnZS4gCgoqKlRhc2tzKio6CgoxLiBSZWFkIGFib3V0IHRoZSBob3RlbCBib29raW5nIGRhdGEsIGBob3RlbHNgLCBvbiB0aGUgW1RpZHkgVHVlc2RheSBwYWdlXShodHRwczovL2dpdGh1Yi5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5L2Jsb2IvbWFzdGVyL2RhdGEvMjAyMC8yMDIwLTAyLTExL3JlYWRtZS5tZCkgaXQgY2FtZSBmcm9tLiBUaGVyZSBpcyBhbHNvIGEgbGluayB0byBhbiBhcnRpY2xlIGZyb20gdGhlIG9yaWdpbmFsIGF1dGhvcnMuIFRoZSBvdXRjb21lIHdlIHdpbGwgYmUgcHJlZGljdGluZyBpcyBjYWxsZWQgYGlzX2NhbmNlbGVkYC4gCiAgLSBXaXRob3V0IGRvaW5nIGFueSBhbmFseXNpcywgd2hhdCBhcmUgc29tZSB2YXJpYWJsZXMgeW91IHRoaW5rIG1pZ2h0IGJlIHByZWRpY3RpdmUgYW5kIHdoeT8KICAKICAKPlByZXZpb3VzIGNhbmNlbGxhdGlvbnMsIHByZXZpb3VzIGJvb2tpbmdzIG5vdCBjYW5jZWxlZCwgcmVzZXJ2ZWQgcm9vbSB0eXBlLCBkZXBvc2l0IHR5cGUsIGRheXMgaW4gd2FpdGxpc3QuIEJlY2F1c2UgY3VzdG9tZXJzIHRoYXQgaGF2ZSBjYW5jZWxsZWQgYmVmb3JlIG1pZ2h0IGJlIG1vcmUgcHJvbXB0IHRvIGNhbmNlbGxpbmcuIFNpbWlsYXJseSwgcGVvcGxlIHRoYXQgYm9vayB0aGVpciBzcG90IGJ1dCBkb24ndCBjYW5jZWwgYXJlIGxlc3MgbGlrZWx5IHRvIGNhbmNlbC4gVGhlIHJvb20gdHlwZSBjb3VsZCBhZmZlY3QgY2FuY2VsYXRpb24sIGlmIGZvciBleGFtcGxlIHRoZSByb29tIGlzIHZlcnkgZXhwZW5zaXZlIGl0IG1pZ2h0IGhhdmUgcmVxdWlyZWQgbW9yZSB0aG91Z2h0IGFuZCB0aGVyZWZvcmUgdGhlIGNsaWVudCBtaWdodCBiZSBsZXNzIGxpa2VseSB0byBjYW5jZWwuIEFsc28sIHBlb3BsZSB0aGF0IG1hZGUgYSBkZXBvc2l0IGFyZSBhbHNvIGVsc3MgbGlrZWx5IHRvIGNhbmNlbC4gTGFzdGx5LCBwZW9wbGUgdGhhdCB3YWl0ZWQgZm9yIGEgbG9uZ2VyIHRpbWUgdG8gZ2V0IGEgcm9vbSBjb3VsZCBiZSBsZXNzIHByb21wdCB0byBjYW5jZWwsIHRoaXMgaXMgdGhlIHNhbWUgY2FzZSBmb3IgdGhlIGxlYWQgdGltZSB2YXJpYWJsZS4gIAoKICBfIFdoYXQgYXJlIHNvbWUgcHJvYmxlbXMgdGhhdCBtaWdodCBleGlzdCB3aXRoIHRoZSBkYXRhPyBZb3UgbWlnaHQgdGhpbmsgYWJvdXQgaG93IGl0IHdhcyBjb2xsZWN0ZWQgYW5kIHdobyBkaWQgdGhlIGNvbGxlY3RpbmcuICAKICAKICAKPlRoZSBkYXRhIG1pZ2h0IG5vdCByZXNwZWN0IHRoZSBjdXN0b21lcnMgZ2l2ZW4gdGhhdCB0aGV5IHdlcmUgbm90IGFza2VkIGZvciBjb25zZW50LCB0aGUgZGF0YSB3YXMgZ2F0aGVyZWQgdXNpbmcgc29mdHdhcmUgYXV0b21hdGljYWxseSB0aGVyZWZvcmUsIHRoZXJlIHdhcyBubyBzcGFjZSBmb3IgYSBkaWFsb2d1ZSBiZXR3ZWVuIHRoZSBzYW1wbGUgcG9wdWxhdGlvbiBhbmQgdGhlIGVudGl0eSBnYXRoZXJpbmcgdGhlIGRhdGEuIAoKICAtIElmIHdlIGNvbnN0cnVjdCBhIG1vZGVsLCB3aGF0IHR5cGUgb2YgY29uY2x1c2lvbnMgd2lsbCBiZSBhYmxlIHRvIGRyYXcgZnJvbSBpdD8gIAoKPldlIHdpbGwgYmUgYWJsZSB0byBpZGVudGlmeSB3aGljaCB2YXJpYWJsZXMgYXJlIHRoZSBtb3N0IGltcG9ydGFudCB3aGVuIHByZWRpY3Rpbmcgd2hhdCBndWVzdHMgYXJlIG1vcmUgbGlrZWx5IHRvIGNhbmNlbC4gCgoKMi4gQ3JlYXRlIHNvbWUgZXhwbG9yYXRvcnkgcGxvdHMgb3IgdGFibGUgc3VtbWFyaWVzIG9mIHRoZSBkYXRhLCBjb25jZW50cmF0aW5nIG1vc3Qgb24gcmVsYXRpb25zaGlwcyB3aXRoIHRoZSByZXNwb25zZSB2YXJpYWJsZS4gS2VlcCBpbiBtaW5kIHRoZSByZXNwb25zZSB2YXJpYWJsZSBpcyBudW1lcmljLCAwIG9yIDEuIFlvdSBtYXkgd2FudCB0byBtYWtlIGl0IGNhdGVnb3JpY2FsICh5b3UgYWxzbyBtYXkgbm90KS4gQmUgc3VyZSB0byBhbHNvIGV4YW1pbmUgbWlzc2luZyB2YWx1ZXMgb3Igb3RoZXIgaW50ZXJlc3RpbmcgdmFsdWVzLiAgCgpRdWFudGl0YXRpdmUgVmFyaWFibGVzIAoKYGBge3J9CmhvdGVscyAlPiUgCiAgc2VsZWN0KHdoZXJlKGlzLm51bWVyaWMpKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBldmVyeXRoaW5nKCksCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gInZhcmlhYmxlIiwgCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJ2YWx1ZSIpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSkpICsKICBnZW9tX2hpc3RvZ3JhbSgpICsKICBmYWNldF93cmFwKHZhcnModmFyaWFibGUpLCAKICAgICAgICAgICAgIHNjYWxlcyA9ICJmcmVlIikKYGBgCgo+VGhlcmUgYXJlIGEgbG90IG9mIGNvdW50cyBvZiAwIGZvciBkaWZmZXJlbnQgdmFyaWFibGVzIGxpa2UgY2hpbGRyZW4sIGJvb2tpbmdzIG5vdCBjYW5jZWxlZCwgcHJldmlvdXMgY2FuY2VsYXRpb25zLCBkYXlzIGluIHdhaXRpbmcgbGlzdCwgaXMgY2FuY2VsbGVkLCBkYXlzIGluIHdhaXRpbmcgbGlzdCwgYmFiaWVzLCBhZHIsIGFkdWx0cywgYW5kIGlzIHJlcGVhdGVkIGdlc3QgdGhlcmVmb3JlIEkgd291bGQgbmVlZCB0byBjcmVhdGUgYW4gaW5kaWNhdG9yIHZhcmlhYmxlIGZvciBlYWNoLiAKQXJyaXZhbCBkYXkgZGF0ZSB3ZWVrIG51bWJlciBoYXMgYSBub3JtYWwgZGlzdHJpYnV0aW9uLCBsZWFkIHRpbWUgaXMgcmlnaHQgc2tld2VkIGFuZCBhcnJpdmFsIGRhdGUgZGF5IG9mIG1vbnRoIGhhcyBhIHByZXR0eSB1bmlmb3JtIGRpc3RyaWJ1dGlvbi4gQWxzbywgYXJyaXZhbCBkYXRlIGRheSBvZiB0aGUgbW9udGggaGFzIHRvbyBtYW55IHVuaXF1ZSBsZXZlbHMsIHRoZXJlZm9yZSwgSSB3b24ndCB1c2UgaXQgYXMgYSB2YXJpYWJsZS4gLiAKCmBgYHtyfQpob3RlbHMgJT4lIAogIGNvdW50KGlzX3JlcGVhdGVkX2d1ZXN0KSAKICAKYGBgCj5XZSBjYW4gc2VlIHRoYXQgb25seSBmZXcgZ3Vlc3RzIGFyZSByZXBlYXRlZCBndWVzdHMuIAoKCmBgYHtyfQpob3RlbHMgJT4lIAogIHNlbGVjdChjaGlsZHJlbiwgcHJldmlvdXNfYm9va2luZ3Nfbm90X2NhbmNlbGVkLCBib29raW5nX2NoYW5nZXMsIHJlcXVpcmVkX2Nhcl9wYXJraW5nX3NwYWNlcywgYmFiaWVzKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IGV2ZXJ5dGhpbmcoKSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAidmFyaWFibGUiLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInZhbHVlIikgJT4lIAogIGdncGxvdChhZXMoeCA9IHZhbHVlKSkgKwogIGdlb21faGlzdG9ncmFtKCkgKwogIGZhY2V0X3dyYXAodmFycyh2YXJpYWJsZSksIAogICAgICAgICAgICAgc2NhbGVzID0gImZyZWUiKQogIApgYGAKCj5JIHJlYWxpc2VkIHRoYXQgdGhlIG51bWJlciBvZiBib29raW5nIGNoYW5nZXMsIHRoZSBudW1iZXIgb2YgY2hpbGRyZW4gYW5kIHRoZSBudW1iZXIgb2YgcHJldmlvdXMgYm9va2luZ3Mgbm90IGNhbmNlbGVkIGhhdmUgbGV2ZWxzIHdpdGggbG93IGNvdW50cyB0aGVyZWZvcmUsIEkgY291bGQgY3JlYXRlIGZld2VyIGxldmVscyBmb3IgdGhlbS4gCgpDYXRlZ29yaWNhbCB2YXJpYWJsZXM6CmBgYHtyfQpob3RlbHMgJT4lIAogIHNlbGVjdCh3aGVyZShpcy5jaGFyYWN0ZXIpKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBldmVyeXRoaW5nKCksCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gInZhcmlhYmxlIiwgCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJ2YWx1ZSIpIApgYGAKCj5Ob25lIG9mIHRoZSB2YXJpYWJsZXMgaXMgYWxyZWFkeSBmb3JtYXR0ZWQgYXMgYSBmYWN0b3IuIE1hbnkgb2YgdGhlbSBoYXZlIGEgY2hhcmFjdGVyIHR5cGUsIHRoaXMgYW1vdW50IHRvIDEsNTUyLDA3MCBvYnNlcnZhdGlvbnMuIEkgd291bGQgaGF2ZSB0byB0cmFuc2Zvcm0gdGhvc2UgdmFyaWFibGVzIGludG8gZmFjdG9ycyB0byBiZSBhYmxlIHRvIHVzZSB0aGUgaW5mb3JtYXRpb24uIAoKCgpgYGB7cn0KaG90ZWxzICU+JSAKICBjb3VudChkZXBvc2l0ID0gZGVwb3NpdF90eXBlKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZGVwb3NpdCwgeSA9IG4pKSArCiAgZ2VvbV9jb2woKQpgYGAKCgo+SSB0aGluayB0aGF0IHRoZSBkZXBvc2l0IHR5cGUgY291bGQgYmUgYSBnb29kIHByZWRpY3RvciBmb3Igd2hldGhlciBvciBub3QgcGVvcGxlIGNhbmNlbCB0aGVpciByZXNlcnZhdGlvbi4gCgoKCmBgYHtyfQpob3RlbHMgJT4lIAogIGFkZF9uX21pc3MoKSAlPiUgCiAgY291bnQobl9taXNzX2FsbCkKYGBgCgo+VGhlcmUgYXJlIDQgbWlzc2luZyB2YWx1ZXMuIAoKCjMuIEZpcnN0LCB3ZSB3aWxsIGRvIGEgY291cGxlIHRoaW5ncyB0byBnZXQgdGhlIGRhdGEgcmVhZHksIGluY2x1ZGluZyBtYWtpbmcgdGhlIG91dGNvbWUgYSBmYWN0b3IgKG5lZWRzIHRvIGJlIHRoYXQgd2F5IGZvciBsb2dpc3RpYyByZWdyZXNzaW9uKSwgcmVtb3ZpbmcgdGhlIHllYXIgdmFyaWFibGUgYW5kIHNvbWUgcmVzZXJ2YXRpb24gc3RhdHVzIHZhcmlhYmxlcywgYW5kIHJlbW92aW5nIG1pc3NpbmcgdmFsdWVzIChub3QgTlVMTHMgYnV0IHRydWUgbWlzc2luZyB2YWx1ZXMpLiBTcGxpdCB0aGUgZGF0YSBpbnRvIGEgdHJhaW5pbmcgYW5kIHRlc3Qgc2V0LCBzdHJhdGlmeWluZyBvbiB0aGUgb3V0Y29tZSB2YXJpYWJsZSwgYGlzX2NhbmNlbGVkYC4gU2luY2Ugd2UgaGF2ZSBhIGxvdCBvZiBkYXRhLCB3ZSdyZSBnb2luZyB0byBzcGxpdCB0aGUgZGF0YSA1MC81MCBiZXR3ZWVuIHRyYWluaW5nIGFuZCB0ZXN0LiBJIGhhdmUgYWxyZWFkeSBgc2V0LnNlZWQoKWAgZm9yIHlvdS4gQmUgc3VyZSB0byB1c2UgYGhvdGVsc19tb2RgIGluIHRoZSBzcGxpdHRpbmcuCgpgYGB7cn0KaG90ZWxzX21vZCA8LSBob3RlbHMgJT4lIAogIG11dGF0ZShpc19jYW5jZWxlZCA9IGFzLmZhY3Rvcihpc19jYW5jZWxlZCkpICU+JSAKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLmNoYXJhY3RlciksIGFzLmZhY3RvcikpICU+JSAKICBzZWxlY3QoLWFycml2YWxfZGF0ZV95ZWFyLAogICAgICAgICAtcmVzZXJ2YXRpb25fc3RhdHVzLAogICAgICAgICAtcmVzZXJ2YXRpb25fc3RhdHVzX2RhdGUpICU+JSAKICBhZGRfbl9taXNzKCkgJT4lIAogIGZpbHRlcihuX21pc3NfYWxsID09IDApICU+JSAKICBzZWxlY3QoLW5fbWlzc19hbGwpCgpzZXQuc2VlZCg0OTQpCgpob3RlbHNfc3BsaXQgPC0gaW5pdGlhbF9zcGxpdChob3RlbHNfbW9kLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyYXRhID0gaXNfY2FuY2VsZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb3AgPSAuNSkKaG90ZWxzX3NwbGl0CiM8dHJhaW5pbmcvdGVzdGluZy90b3RhbD4KCmhvdGVsc190cmFpbmluZyA8LSB0cmFpbmluZyhob3RlbHNfc3BsaXQpCmhvdGVsc190ZXN0aW5nIDwtIHRlc3RpbmcoaG90ZWxzX3NwbGl0KQpgYGAKCjQuIEluIHRoaXMgbmV4dCBzdGVwLCB3ZSBhcmUgZ29pbmcgdG8gZG8gdGhlIHByZS1wcm9jZXNzaW5nLiBVc3VhbGx5LCBJIHdvbid0IHRlbGwgeW91IGV4YWN0bHkgd2hhdCB0byBkbyBoZXJlLCBidXQgZm9yIHlvdXIgZmlyc3QgZXhlcmNpc2UsIEknbGwgdGVsbCB5b3UgdGhlIHN0ZXBzLiAKCiogU2V0IHVwIHRoZSByZWNpcGUgd2l0aCBgaXNfY2FuY2VsZWRgIGFzIHRoZSBvdXRjb21lIGFuZCBhbGwgb3RoZXIgdmFyaWFibGVzIGFzIHByZWRpY3RvcnMgKEhJTlQ6IGB+LmApLiAgCiogVXNlIGEgYHN0ZXBfWFhYKClgIGZ1bmN0aW9uIG9yIGZ1bmN0aW9ucyAoSSB0aGluayB0aGVyZSBhcmUgb3RoZXIgd2F5cyB0byBkbyB0aGlzLCBidXQgSSBmb3VuZCBgc3RlcF9tdXRhdGVfYXQoKWAgZWFzaWVzdCkgdG8gY3JlYXRlIHNvbWUgaW5kaWNhdG9yIHZhcmlhYmxlcyBmb3IgdGhlIGZvbGxvd2luZyB2YXJpYWJsZXM6IGBjaGlsZHJlbmAsIGBiYWJpZXNgLCBhbmQgYHByZXZpb3VzX2NhbmNlbGxhdGlvbnNgLiBTbywgdGhlIG5ldyB2YXJpYWJsZSBzaG91bGQgYmUgYSAxIGlmIHRoZSBvcmlnaW5hbCBpcyBtb3JlIHRoYW4gMCBhbmQgMCBvdGhlcndpc2UuIE1ha2Ugc3VyZSB5b3UgZG8gdGhpcyBpbiBhIHdheSB0aGF0IGFjY291bnRzIGZvciB2YWx1ZXMgdGhhdCBtYXkgYmUgbGFyZ2VyIHRoYW4gYW55IHdlIHNlZSBpbiB0aGUgZGF0YXNldC4gIAoqIEZvciB0aGUgYGFnZW50YCBhbmQgYGNvbXBhbnlgIHZhcmlhYmxlcywgbWFrZSBuZXcgaW5kaWNhdG9yIHZhcmlhYmxlcyB0aGF0IGFyZSAxIGlmIHRoZXkgaGF2ZSBhIHZhbHVlIG9mIGBOVUxMYCBhbmQgMCBvdGhlcndpc2UuIAoqIFVzZSBgZmN0X2x1bXBfbigpYCB0byBsdW1wIHRvZ2V0aGVyIGNvdW50cmllcyB0aGF0IGFyZW4ndCBpbiB0aGUgdG9wIDUgbW9zdCBvY2N1cnJpbmcuIAoqIElmIHlvdSB1c2VkIG5ldyBuYW1lcyBmb3Igc29tZSBvZiB0aGUgbmV3IHZhcmlhYmxlcyB5b3UgY3JlYXRlZCwgdGhlbiByZW1vdmUgYW55IHZhcmlhYmxlcyB0aGF0IGFyZSBubyBsb25nZXIgbmVlZGVkLiAKKiBVc2UgYHN0ZXBfbm9ybWFsaXplKClgIHRvIGNlbnRlciBhbmQgc2NhbGUgYWxsIHRoZSBub24tY2F0ZWdvcmljYWwgcHJlZGljdG9yIHZhcmlhYmxlcy4gKERvIHRoaXMgQkVGT1JFIGNyZWF0aW5nIGR1bW15IHZhcmlhYmxlcy4gV2hlbiBJIHRyaWVkIHRvIGRvIGl0IGFmdGVyLCBJIHJhbiBpbnRvIGFuIGVycm9yIC0gSSdtIHN0aWxsIGludmVzdGlnYXRpbmcgd2h5LikKKiBDcmVhdGUgZHVtbXkgdmFyaWFibGVzIGZvciBhbGwgZmFjdG9ycy9jYXRlZ29yaWNhbCBwcmVkaWN0b3IgdmFyaWFibGVzIChtYWtlIHN1cmUgeW91IGhhdmUgYC1hbGxfb3V0Y29tZXMoKWAgaW4gdGhpcyBwYXJ0ISEpLiAgCiogVXNlIHRoZSBgcHJlcCgpYCBhbmQgYGp1aWNlKClgIGZ1bmN0aW9ucyB0byBhcHBseSB0aGUgc3RlcHMgdG8gdGhlIHRyYWluaW5nIGRhdGEganVzdCB0byBjaGVjayB0aGF0IGV2ZXJ5dGhpbmcgd2VudCBhcyBwbGFubmVkLgoKCmBgYHtyfQpob3RlbHNfcmVjaXBlIDwtIHJlY2lwZShpc19jYW5jZWxlZCB+IC4sCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGhvdGVsc190cmFpbmluZykgJT4lIAogIHN0ZXBfbXV0YXRlX2F0KGNoaWxkcmVuLGJhYmllcyxwcmV2aW91c19jYW5jZWxsYXRpb25zLCBmbiA9IH5hcy5udW1lcmljKC4+MCApKSAlPiUgCiAgc3RlcF9tdXRhdGVfYXQoYWdlbnQsY29tcGFueSwgZm4gPSB+YXMubnVtZXJpYyguPT0iTlVMTCIgKSkgJT4lIAogIHN0ZXBfbXV0YXRlX2F0KGNvdW50cnksIGZuID0gfiBmY3RfbHVtcF9uKGNvdW50cnksIG4gPSA1KSkgJT4lIAogIHN0ZXBfbm9ybWFsaXplKGFsbF9wcmVkaWN0b3JzKCksIAogICAgICAgICAgICAgICAgIC1hbGxfbm9taW5hbCgpKSAlPiUgCiAgc3RlcF9kdW1teShhbGxfbm9taW5hbCgpLAogICAgICAgICAgICAgLWFsbF9vdXRjb21lcygpKSAKICAKCgogIApgYGAKCgpgYGB7cn0KaG90ZWxzX3JlY2lwZSAlPiUgCiAgcHJlcChob3RlbHNfdHJhaW5pbmcpICU+JQogIGp1aWNlKCkgCmBgYAoKNS4gSW4gdGhpcyBzdGVwIHdlIHdpbGwgc2V0IHVwIGEgTEFTU08gbW9kZWwgYW5kIHdvcmtmbG93LgoKKiBJbiBnZW5lcmFsLCB3aHkgd291bGQgd2Ugd2FudCB0byB1c2UgTEFTU08gaW5zdGVhZCBvZiByZWd1bGFyIGxvZ2lzdGljIHJlZ3Jlc3Npb24/IChISU5UOiB0aGluayBhYm91dCB3aGF0IGhhcHBlbnMgdG8gdGhlIGNvZWZmaWNpZW50cykuICAKCgo+QmVjYXVzZSB3ZSB3YW50IHRvIGlkZW50aWZ5IGNvZWZmaWNpZW50cyB0aGF0IG1pbmltaXplIGEgcGVuYWxpemVkIHZlcnNpb24gb2YgdGhlIHN1bSBvZiBzcXVhcmVkIGVycm9ycy4gV2Ugd2FudCB0byBwZW5hbGl6ZSB2YXJpYWJsZXMgdGhhdCBhcmUgbm9uLWluZm9ybWF0aXZlIGFuZCBzaHJpbmtzIHRoZW0gdG93YXJkcyAwLgoKCiogRGVmaW5lIHRoZSBtb2RlbCB0eXBlLCBzZXQgdGhlIGVuZ2luZSwgc2V0IHRoZSBgcGVuYWx0eWAgYXJndW1lbnQgdG8gYHR1bmUoKWAgYXMgYSBwbGFjZWhvbGRlciwgYW5kIHNldCB0aGUgbW9kZS4gIAoKYGBge3J9CmhvdGVsc19sYXNzb19tb2QgPC0gCiAgbG9naXN0aWNfcmVnKG1peHR1cmUgPSAxKSAlPiUgCiAgc2V0X2VuZ2luZSgiZ2xtbmV0IikgJT4lIAogIHNldF9hcmdzKHBlbmFsdHkgPSB0dW5lKCkpICU+JSAKICBzZXRfbW9kZSgiY2xhc3NpZmljYXRpb24iKQpgYGAKCiogQ3JlYXRlIGEgd29ya2Zsb3cgd2l0aCB0aGUgcmVjaXBlIGFuZCBtb2RlbC4gIAoKYGBge3J9CmhvdGVsc19sYXNzb193ZiA8LSAKICB3b3JrZmxvdygpICU+JSAKICBhZGRfcmVjaXBlKGhvdGVsc19yZWNpcGUpICU+JSAKICBhZGRfbW9kZWwoaG90ZWxzX2xhc3NvX21vZCkKCmhvdGVsc19sYXNzb193ZgpgYGAKCgo2LiBJbiB0aGlzIHN0ZXAsIHdlJ2xsIHR1bmUgdGhlIG1vZGVsIGFuZCBmaXQgdGhlIG1vZGVsIHVzaW5nIHRoZSBiZXN0IHR1bmluZyBwYXJhbWV0ZXIgdG8gdGhlIGVudGlyZSB0cmFpbmluZyBkYXRhc2V0LgoKKiBDcmVhdGUgYSA1LWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiBzYW1wbGUuIFdlJ2xsIHVzZSB0aGlzIGxhdGVyLiBJIGhhdmUgc2V0IHRoZSBzZWVkIGZvciB5b3UuICAKKiBVc2UgdGhlIGBncmlkX3JlZ3VsYXIoKWAgZnVuY3Rpb24gdG8gY3JlYXRlIGEgZ3JpZCBvZiAxMCBwb3RlbnRpYWwgcGVuYWx0eSBwYXJhbWV0ZXJzICh3ZSdyZSBrZWVwaW5nIHRoaXMgc29ydCBvZiBzbWFsbCBiZWNhdXNlIHRoZSBkYXRhc2V0IGlzIHByZXR0eSBsYXJnZSkuIFVzZSB0aGF0IHdpdGggdGhlIDUtZm9sZCBjdiBkYXRhIHRvIHR1bmUgdGhlIG1vZGVsLiAgCiogVXNlIHRoZSBgdHVuZV9ncmlkKClgIGZ1bmN0aW9uIHRvIGZpdCB0aGUgbW9kZWxzIHdpdGggZGlmZmVyZW50IHR1bmluZyBwYXJhbWV0ZXJzIHRvIHRoZSBkaWZmZXJlbnQgY3Jvc3MtdmFsaWRhdGlvbiBzZXRzLiAgCiogVXNlIHRoZSBgY29sbGVjdF9tZXRyaWNzKClgIGZ1bmN0aW9uIHRvIGNvbGxlY3QgYWxsIHRoZSBtZXRyaWNzIGZyb20gdGhlIHByZXZpb3VzIHN0ZXAgYW5kIGNyZWF0ZSBhIHBsb3Qgd2l0aCB0aGUgYWNjdXJhY3kgb24gdGhlIHktYXhpcyBhbmQgdGhlIHBlbmFsdHkgdGVybSBvbiB0aGUgeC1heGlzLiBQdXQgdGhlIHgtYXhpcyBvbiB0aGUgbG9nIHNjYWxlLiAgCiogVXNlIHRoZSBgc2VsZWN0X2Jlc3QoKWAgZnVuY3Rpb24gdG8gZmluZCB0aGUgYmVzdCB0dW5pbmcgcGFyYW1ldGVyLCBmaXQgdGhlIG1vZGVsIHVzaW5nIHRoYXQgdHVuaW5nIHBhcmFtZXRlciB0byB0aGUgZW50aXJlIHRyYWluaW5nIHNldCAoSElOVDogYGZpbmFsaXplX3dvcmtmbG93KClgIGFuZCBgZml0KClgKSwgYW5kIGRpc3BsYXkgdGhlIG1vZGVsIHJlc3VsdHMgdXNpbmcgYHB1bGxfd29ya2Zsb3dfZml0KClgIGFuZCBgdGlkeSgpYC4gQXJlIHRoZXJlIHNvbWUgdmFyaWFibGVzIHdpdGggY29lZmZpY2llbnRzIG9mIDA/CgpgYGB7cn0Kc2V0LnNlZWQoNDk0KSAjIGZvciByZXByb2R1Y2liaWxpdHkKaG90ZWxzX2N2IDwtIHZmb2xkX2N2KGhvdGVsc190cmFpbmluZywgdiA9IDUpCmBgYAoKCmBgYHtyfQpwZW5hbHR5X2dyaWQgPC0gZ3JpZF9yZWd1bGFyKHBlbmFsdHkoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSAxMCkKYGBgCgoKCmBgYHtyfQpob3RlbHNfbGFzc29fdHVuZSA8LSAKICBob3RlbHNfbGFzc29fd2YgJT4lIAogIHR1bmVfZ3JpZCgKICAgIHJlc2FtcGxlcyA9IGhvdGVsc19jdiwKICAgIGdyaWQgPSBwZW5hbHR5X2dyaWQKICAgICkKaG90ZWxzX2xhc3NvX3R1bmUKCmBgYAoKCgpgYGB7cn0KaG90ZWxzX2xhc3NvX3R1bmUgJT4lIAogIGNvbGxlY3RfbWV0cmljcygpCmBgYAoKYGBge3J9CmhvdGVsc19sYXNzb190dW5lICU+JSAKICBjb2xsZWN0X21ldHJpY3MoKSAlPiUgCiAgZmlsdGVyKC5tZXRyaWMgPT0gImFjY3VyYWN5IikgJT4lIAogIGdncGxvdChhZXMoeCA9IHBlbmFsdHksIHkgPSBtZWFuKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9saW5lKCkgKwogIHNjYWxlX3hfbG9nMTAoCiAgIGJyZWFrcyA9IHNjYWxlczo6dHJhbnNfYnJlYWtzKCJsb2cxMCIsIGZ1bmN0aW9uKHgpIDEwXngpLAogICBsYWJlbHMgPSBzY2FsZXM6OnRyYW5zX2Zvcm1hdCgibG9nMTAiLHNjYWxlczo6bWF0aF9mb3JtYXQoMTBeLngpKSkgKwogIGxhYnMoeCA9ICJwZW5hbHR5IiwgeSA9ICJhY2N1cmFjeSIpCmBgYAoKCmBgYHtyfQpob3RlbHNfbGFzc29fdHVuZSAlPiUgCiAgc2hvd19iZXN0KG1ldHJpYyA9ICJhY2N1cmFjeSIpCmBgYAoKYGBge3J9CmJlc3RfcGFyYW0gPC0gaG90ZWxzX2xhc3NvX3R1bmUgJT4lIAogIHNlbGVjdF9iZXN0KG1ldHJpYyA9ICJhY2N1cmFjeSIpCmJlc3RfcGFyYW0KYGBgCmBgYHtyfQpob3RlbHNfbGFzc29fZmluYWxfd2YgPC0gaG90ZWxzX2xhc3NvX3dmICU+JSAKICBmaW5hbGl6ZV93b3JrZmxvdyhiZXN0X3BhcmFtKQpob3RlbHNfbGFzc29fZmluYWxfd2YKYGBgCmBgYHtyfQpob3RlbHNfbGFzc29fZmluYWxfbW9kIDwtIGhvdGVsc19sYXNzb19maW5hbF93ZiAlPiUgCiAgZml0KGRhdGEgPSBob3RlbHNfdHJhaW5pbmcpCgpob3RlbHNfbGFzc29fZmluYWxfbW9kICU+JSAKICBwdWxsX3dvcmtmbG93X2ZpdCgpICU+JSAKICB0aWR5KCkgCmBgYAoKCj5UaGUgdmFyaWFibGVzIHdpdGggYSAwIGNvZWZmaWNpZW50IGFyZSBiYWJpZXMsIGFycml2YWxfZGF0ZV9tb250aF9PY3RvYmVyLCBtYXJrZXRfc2VnbWVudF9Db3Jwb3JhdGUsIG1hcmtldF9zZWdtZW50X1VuZGVmaW5lZCwgZGlzdHJpYnV0aW9uX2NoYW5uZWxfVW5kZWZpbmVkLGFzc2lnbmVkX3Jvb21fdHlwZV9QLgoKNy4gTm93IHRoYXQgd2UgaGF2ZSBhIG1vZGVsLCBsZXQncyBldmFsdWF0ZSBpdCBhIGJpdCBtb3JlLiBBbGwgd2UgaGF2ZSBsb29rZWQgYXQgc28gZmFyIGlzIHRoZSBjcm9zcy12YWxpZGF0ZWQgYWNjdXJhY3kgZnJvbSB0aGUgcHJldmlvdXMgc3RlcC4gCgoqIENyZWF0ZSBhIHZhcmlhYmxlIGltcG9ydGFuY2UgZ3JhcGguIFdoaWNoIHZhcmlhYmxlcyBzaG93IHVwIGFzIHRoZSBtb3N0IGltcG9ydGFudD8gQXJlIHlvdSBzdXJwcmlzZWQ/ICAKCmBgYHtyfQpob3RlbHNfbGFzc29fZmluYWxfbW9kICU+JSAKICBwdWxsX3dvcmtmbG93X2ZpdCgpICU+JSAKICB2aXAoKQpgYGAKCgo+VGhlIG1vc3QgaW1wb3J0YW50IHNlZW0gdG8gYmUgcmVzZXJ2ZWQgcm9vbSB0eXBlIFAsIGFzc2lnbmVkIHJvb20gdHlwZSBJLCBhbmQgZGVwb3NpdCB0eXBlIE5vbi5SZWZ1bmQuIEkgYW0gbm90IHN1cnByaXNlZCwgSSB0aGluayB0aGF0IHJlc2VydmluZyBhIGNlcnRhaW4gdHlwZSBvZiByb29tIGNvdWxkIG1ha2UgZ3Vlc3RzIG1vcmUgcHJvbXB0IHRvIGNhbmNlbCB0aGUgcmVzZXJ2YXRpb24gZWFzaWx5IHJlbGF0aXZlIHRvIG90aGVyIHR5cGVzLiBBbHNvLCBiZWluZyBhc3NpZ25lZCB0byBhIHJvb20gdHlwZSB0aGF0IHRoZSBndWVzdHMgZG9uJ3QgbGlrZSBzb3VuZHMgbGlrZSBhIHJlYXNvbmFibGUgcHJlZHVjdG9yIGZvciBjYW5jZWxsaW5nLiBMYXN0bHksIGFzIHdlIHByZXZpb3VzbHkgbWVudGlvbmVkLCB0aGUgcmVmdW5kYWJpbGl0eSBvZiB0aGUgZGVwb3NpdCBjb3VsZCBhZmZlY3QgdGhlIGRlY2lzaW9uIG9mIGEgZ3Vlc3QgdG8gY2FuY2VsIHRoZWlyIGJvb2tpbmcuIAoKCiogVXNlIHRoZSBgbGFzdF9maXQoKWAgZnVuY3Rpb24gdG8gZml0IHRoZSBmaW5hbCBtb2RlbCBhbmQgdGhlbiBhcHBseSBpdCB0byB0aGUgdGVzdGluZyBkYXRhLiBSZXBvcnQgdGhlIG1ldHJpY3MgZnJvbSB0aGUgdGVzdGluZyBkYXRhIHVzaW5nIHRoZSBgY29sbGV0X21ldHJpY3MoKWAgZnVuY3Rpb24uIEhvdyBkbyB0aGV5IGNvbXBhcmUgdG8gdGhlIGNyb3NzLXZhbGlkYXRlZCBtZXRyaWNzPwoKYGBge3J9CmhvdGVsc19sYXNzb190ZXN0IDwtIGhvdGVsc19sYXNzb19maW5hbF93ZiAlPiUgCiAgbGFzdF9maXQoaG90ZWxzX3NwbGl0KQoKaG90ZWxzX2xhc3NvX3Rlc3QgJT4lIAogIGNvbGxlY3RfbWV0cmljcygpCgpob3RlbHNfbGFzc29fdHVuZSAlPiUgCiAgY29sbGVjdF9tZXRyaWNzKCkKCgpgYGAKCgo+VGhleSBhcmUgdmVyeSBzaW1pbGFyIGZvciBib3RoIGFjY3VyYWN5IGFuZCByb2NfYXVjIG1ldHJpY3MuIAoKKiBVc2UgdGhlIGBjb2xsZWN0X3ByZWRpY3Rpb25zKClgIGZ1bmN0aW9uIHRvIGZpbmQgdGhlIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIGFuZCBjbGFzc2VzIGZvciB0aGUgdGVzdCBkYXRhLiBTYXZlIHRoaXMgdG8gYSBuZXcgZGF0YXNldCBjYWxsZWQgYHByZWRzYC4gVGhlbiwgdXNlIHRoZSBgY29uZl9tYXQoKWAgZnVuY3Rpb24gZnJvbSBgZGlhbHNgIChwYXJ0IG9mIGB0aWR5bW9kZWxzYCkgdG8gY3JlYXRlIGEgY29uZnVzaW9uIG1hdHJpeCBzaG93aW5nIHRoZSBwcmVkaWN0ZWQgY2xhc3NlcyB2cy4gdGhlIHRydWUgY2xhc3Nlcy4gV2hhdCBpcyB0aGUgdHJ1ZSBwb3NpdGl2ZSByYXRlIChzZW5zaXRpdml0eSk/IFdoYXQgaXMgdGhlIHRydWUgbmVnYXRpdmUgcmF0ZSAoc3BlY2lmaWNpdHkpPyBTZWUgdGhpcyBbV2lraXBlZGlhXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Db25mdXNpb25fbWF0cml4KSByZWZlcmVuY2UgaWYgeW91IChsaWtlIG1lKSB0ZW5kIHRvIGZvcmdldCB0aGVzZSBkZWZpbml0aW9ucy4KCmBgYHtyfQpwcmVkcyA8LSAKICBjb2xsZWN0X3ByZWRpY3Rpb25zKGhvdGVsc19sYXNzb190ZXN0KQpwcmVkcyAlPiUgCiAgY29uZl9tYXQoaXNfY2FuY2VsZWQsLnByZWRfY2xhc3MpCgpgYGAKYGBge3J9CiNTZW5zaXRpdml0eToKMzQzNzIvKDM0MzcyKzMyMTEpCgojU3BlY2lmaWNpdHk6CjE0MjcxLyg3ODM5KzE0MjcxKQoKYGBgCgoKPlNlbnNpdGl2aXR5IGlzIDAuOTE0NTYyNCBhbmQgU3BlY2lmaWNpdHkgaXMgMC42NDU0NTQ1LgoKKiBVc2UgdGhlIGBwcmVkc2AgZGF0YXNldCB5b3UganVzdCBjcmVhdGVkIHRvIGNyZWF0ZSBhIGRlbnNpdHkgcGxvdCBvZiB0aGUgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgb2YgY2FuY2VsaW5nICh0aGUgdmFyaWFibGUgaXMgY2FsbGVkIGAucHJlZF8xYCksIGZpbGxpbmcgYnkgYGlzX2NhbmNlbGVkYC4gVXNlIGFuIGBhbHBoYSA9IC41YCBhbmQgYGNvbG9yID0gTkFgIGluIHRoZSBgZ2VvbV9kZW5zaXR5KClgLiBBbnN3ZXIgdGhlc2UgcXVlc3Rpb25zOiBhLiBXaGF0IHdvdWxkIHRoaXMgZ3JhcGggbG9vayBsaWtlIGZvciBhIG1vZGVsIHdpdGggYW4gYWNjdXJhY3kgdGhhdCB3YXMgY2xvc2UgdG8gMT8gYi4gT3VyIHByZWRpY3Rpb25zIGFyZSBjbGFzc2lmaWVkIGFzIGNhbmNlbGVkIGlmIHRoZWlyIHByZWRpY3RlZCBwcm9iYWJpbGl0eSBvZiBjYW5jZWxpbmcgaXMgZ3JlYXRlciB0aGFuIC41LiBJZiB3ZSB3YW50ZWQgdG8gaGF2ZSBhIGhpZ2ggdHJ1ZSBwb3NpdGl2ZSByYXRlLCBzaG91bGQgd2UgbWFrZSB0aGUgY3V0b2ZmIGZvciBwcmVkaWN0ZWQgYXMgY2FuY2VsZWQgaGlnaGVyIG9yIGxvd2VyIHRoYW4gLjU/IGMuIFdoYXQgaGFwcGVucyB0byB0aGUgdHJ1ZSBuZWdhdGl2ZSByYXRlIGlmIHdlIHRyeSB0byBnZXQgYSBoaWdoZXIgdHJ1ZSBwb3NpdGl2ZSByYXRlPyAKCgpgYGB7cn0KcHJlZHMgJT4lIAogIGdncGxvdChhZXMoeCA9IC5wcmVkXzEsIGZpbGwgPSBpc19jYW5jZWxlZCkpICsKICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAuNSwgCiAgICAgICAgICAgICBjb2xvciA9IE5BKSArCiAgbGFicyh4ID0gIlByb2JhYmlsaXRpZXMiLCAKICAgICAgIHkgPSAiRGVuc2l0eSIsCiAgICAgICB0aXRsZSA9ICAiUHJlZGljdGVkIHByb2JhYmlsaXRpZXMgb2YgY2FuY2VsaW5nIikKYGBgCgoKPmEuIFRoZSBncmFwaCB3b3VsZCBoYXZlIGRlbnNpdGllcyBjbHVzdGVyZWQgb24gdGhlIGV4dHJlbWVzIGNvbnNpZGVyaW5nIHRoYXQgd2Ugd291bGQgYmUgYWJsZSB0byBwcmVkaWN0IHdpdGggbW9yZSBjZXJ0YWludHkgd2hldGhlciBvciBub3QgdGhlIGd1ZXN0IGNhbmNlbGxlZC4gCj5iLkxvd2VyIHRoYW4gMC41LCBzaW5jZSBhIGxvd2VyIGN1dC1vZmYgcHJvbW90ZXMgYSBoaWdoZXIgc2Vuc2l0aXZpdHkgYW5kIGxvd2VyIHNwZWNpZmljaXR5LiAKPmMuVGhlIHRydWUgbmVnYXRpdmUgd291bGQgdGhlbiBiZSBsb3dlci4gCgo4LiBMZXQncyBzYXkgdGhhdCB0aGlzIG1vZGVsIGlzIGdvaW5nIHRvIGJlIGFwcGxpZWQgdG8gYm9va2luZ3MgMTQgZGF5cyBpbiBhZHZhbmNlIG9mIHRoZWlyIGFycml2YWwgYXQgZWFjaCBob3RlbCwgYW5kIHNvbWVvbmUgd2hvIHdvcmtzIGZvciB0aGUgaG90ZWwgd2lsbCBtYWtlIGEgcGhvbmUgY2FsbCB0byB0aGUgcGVyc29uIHdobyBtYWRlIHRoZSBib29raW5nLiBEdXJpbmcgdGhpcyBwaG9uZSBjYWxsLCB0aGV5IHdpbGwgdHJ5IHRvIGFzc3VyZSB0aGF0IHRoZSBwZXJzb24gd2lsbCBiZSBrZWVwaW5nIHRoZWlyIHJlc2VydmF0aW9uIG9yIHRoYXQgdGhleSB3aWxsIGJlIGNhbmNlbGluZyBpbiB3aGljaCBjYXNlIHRoZXkgY2FuIGRvIHRoYXQgbm93IGFuZCBzdGlsbCBoYXZlIHRpbWUgdG8gZmlsbCB0aGUgcm9vbS4gSG93IHNob3VsZCB0aGUgaG90ZWwgZ28gYWJvdXQgZGVjaWRpbmcgd2hvIHRvIGNhbGw/IEhvdyBjb3VsZCB0aGV5IG1lYXN1cmUgd2hldGhlciBpdCB3YXMgd29ydGggdGhlIGVmZm9ydCB0byBkbyB0aGUgY2FsbGluZz8gQ2FuIHlvdSB0aGluayBvZiBhbm90aGVyIHdheSB0aGV5IG1pZ2h0IHVzZSB0aGUgbW9kZWw/IAoKCj5UaGV5IHNob3VsZCBjYWxsIHRoZSBjbGllbnRzIHRoYXQgYm9va2VkIGEgcm9vbSBvZiB0aGUgdHlwZSB0aGF0IHdlIGlkZW50aWZpZWQgYXMgdmFyaWFibGVzIG9mIGltcG9ydGFuY2UgZm9yIHRoZSBwcmVkaWN0aW9uIG9mIHdoZXRoZXIgdGhleSBjYW5jZWwgb3Igbm90LiBBbHNvIHRoZXkgc2hvdWxkIGNhbGwgdGhlIGNsaWVudHMgdGhhdCBoYXZlIGEgbm9uLXJlZnVuZGFibGUgZGVwb3NpdC4gVGhleSBjb3VsZCBtZWFzdXJlIHRoZSB2YWx1ZSBvZiB0aGlzIHdvcmsgYnkgY29tcGFyaW5nIHRoZSBwcm9wb3J0aW9uIG9mIGNhbmNlbGxhdGlvbnMgZnJvbSBvbmUgeWVhciB0byB0aGUgb3RoZXIgYW5kIHRoZSBhc3NvY2lhdGVkIG1vbmV0YXJ5IGVmZmVjdHMgb2YgdGhlIGNoYW5nZXMuIFRoZXkgY291bGQgdXNlIHRoaXMgbW9kZWwgdG8gYmV0dGVyIGFkdmVydGlzZSBvciBtYWtlIG1vcmUgYXBwZWFsaW5nIHRoZSByb29tcyB0aGF0IHdlcmUgY29uc2lkZXJlZCBhcyByZWxldmFudCBwcmVkaWN0b3JzIGZvciBjYW5jZWxhdGlvbi4gCgo5LiBIb3cgbWlnaHQgeW91IGdvIGFib3V0IHF1ZXN0aW9uaW5nIGFuZCBldmFsdWF0aW5nIHRoZSBtb2RlbCBpbiB0ZXJtcyBvZiBmYWlybmVzcz8gQXJlIHRoZXJlIGFueSBxdWVzdGlvbnMgeW91IHdvdWxkIGxpa2UgdG8gYXNrIG9mIHRoZSBwZW9wbGUgd2hvIGNvbGxlY3RlZCB0aGUgZGF0YT8gCgo+SSB3b3VsZCBxdWVzdGlvbiB0aGUgZ2VvZ3JhcGhpYyBkaXN0cmlidXRpb25zIG9mIHRoZSBsb2NhdGlvbnMgb2YgdGhlIGhvdGVscyB0aGF0IGNvdWxkIGRlZmluZSBzcGVjaWZpYyB0cmVuZHMsIHByZWZlcmVuY2VzIG9yIGluIHRoZSBjYXNlIG9mIG91ciBtb2RlbCwgZGVmaW5lIGEgc3BlY2lmaWMgY291bnRyeSBvZiBvcmlnaW4gYXMgcmVsZXZhbnQgLiBJIHdvdWxkIGxpa2UgdG8gYXNrIHdoZXRoZXIgb3Igbm90IHRoZXkgbGV0IHRoZSBndWVzdHMga25vdyB0aGUgZGF0YSB3YXMgYmVpbmcgY29sbGVjdGVkIGFuZCBhbHNvLCBob3cgdGhleSBwZXJzdWFkZWQgaG90ZWxzJyBhZG1pbmlzdHJhdGlvbiB0byBhbGxvdyB0aGVtIHRvIGdhdGhlciB0aGUgZGF0YS4gQWxzbywgSSB3b3VsZCBsaWtlIHRvIGFzayB0aGVtIHdoYXQgd2FzIHRoZSBwdXJwb3NlIG9mIHRoaXMgd29yay4gCgoKCgoKIyMgQmlhcyBhbmQgRmFpcm5lc3MKCkxpc3RlbiB0byBEci4gUmFjaGVsIFRob21hcydzICBbQmlhcyBhbmQgRmFpcm5lc3MgbGVjdHVyZV0oaHR0cHM6Ly9ldGhpY3MuZmFzdC5haS92aWRlb3MvP2xlc3Nvbj0yKS4gV3JpdGUgYSBicmllZiBwYXJhZ3JhcGggcmVmbGVjdGluZyBvbiBpdC4gWW91IG1pZ2h0IGFsc28gYmUgaW50ZXJlc3RlZCBpbiByZWFkaW5nIHRoZSBbUHJvUHVibGljYSBhcnRpY2xlXShodHRwczovL3d3dy5wcm9wdWJsaWNhLm9yZy9hcnRpY2xlL21hY2hpbmUtYmlhcy1yaXNrLWFzc2Vzc21lbnRzLWluLWNyaW1pbmFsLXNlbnRlbmNpbmcpIERyLiBUaG9tYXMgcmVmZXJlbmNlcyBhYm91dCB1c2luZyBhIHRvb2wgY2FsbGVkIENPTVBBUyB0byBwcmVkaWN0IHJlY2lkaXZpc20uIFNvbWUgcXVlc3Rpb25zL2lkZWFzIHlvdSBtaWdodCBrZWVwIGluIG1pbmQ6CgoKKiBEaWQgeW91IGhlYXIgYW55dGhpbmcgdGhhdCBzdXJwcmlzZWQgeW91PyAgCiogV2h5IGlzIGl0IGltcG9ydGFudCB0aGF0IHdlIHBheSBhdHRlbnRpb24gdG8gYmlhcyBhbmQgZmFpcm5lc3Mgd2hlbiBzdHVkeWluZyBkYXRhIHNjaWVuY2U/ICAKKiBJcyB0aGVyZSBhIHR5cGUgb2YgYmlhcyBEci4gVGhvbWFzIGRpc2N1c3NlZCB0aGF0IHdhcyBuZXcgdG8geW91PyBDYW4geW91IHRoaW5rIGFib3V0IHBsYWNlcyB5b3UgaGF2ZSBzZWVuIHRoZXNlIHR5cGVzIG9mIGJpYXNlcz8KCgo+SSB3YXMgc3VycHJpc2VkIGJ5IGhvdyBibGluZGluZyBjYW4gYmUgdGhlIHVzZSBvZiBtYWNoaW5lcyBhbmQgYWxnb3J5dGhtcyB3aGVuIGRlYWxpbmcgd2l0aCBzb2NpYWxseSByZWxldmFudCBpc3N1ZXMuIEZlZWQtYmFjayBsb29wcyBjYW4gYmlhcyBvdXIgbW9kZWxzIHByb3ZpZGluZyBmaW5kaW5ncyB0aGF0IGFyZSBzZWxmLWZ1bGZpbGxpbmcgdGhhdCBtaWdodCBzZWVtIGZhaXIuIEkgd2FzIGFsc28gc3VycHJpc2VkIHRoYXQgSGlzdG9yaWNhbCBiaWFzIGNvdWxkbid0IGJlIG1pdGlnYXRlZCBhcyBpdCBpcyBhIHN0cnVjdHVyYWwgcHJvYmxlbS4gVGhlcmVmb3JlLCBzdHJ1Y3R1cmFsIHNvY2lhbCBwcm9ibGVtcyBjYW4gcG90ZW50aWFsbHkgYWx3YXlzIHBlcm1lYXRlIGluIGFueSB3b3JrIG9mIHNvY2lhbCByZWxldmFuY2UuIEkgaGF2ZSBzZWVuIHRoZXNlIHByb2JsZW1zIGFyaXNlIGluIHBvbGl0aWNhbCBjb252ZXJzYXRpb25zLCB3aGVuIHBlb3BsZSBxdW90ZSBkaWZmZXJlbnQgc3R1ZGllcyBhbmQgZGF0YSBzb3VyY2VzIHRoYXQgc2VlbSBsZWdpdGltYXRlLCBidXQgZG9uJ3QgcXVlc3Rpb24gd2hldGhlciBvciBub3QgdGhlcmUgd2VyZSBhbnkgYmlhc2VzIG9uIHRoZWlyIHNhbXBsaW5nLiBOb3QgdGFsa2luZyB0byBkb21haW4gZXhwZXJ0cyBhbmQgdGhvc2UgaW1wYWN0ZWQgY2FuIGxlYWQgdG8gcHJvYmxlbWF0aWMgY29uY2x1c2lvbnMuIEEgc2ltaWxhciBjb25zZXF1ZW5jZSBjYW4gYmUgRmFpcndhc2hpbmcsIHdoaWNoIGlzIG1ha2luZyBhIGZhaXIgZXhwbGFuYXRpb24gdG8ganVzdGlmeSB1bmZhaXIgZmluZGluZ3MuIExhc3RseSwgb25lIHF1b3RlIHRoYXQgSSBhbHNvIGxpa2VkIGlzIHRoYXQgc29tZXRpbWVzIHRoZSBhbnN3ZXIgaXMgbm90IHRvIGJ1aWxkLiBEYXRhIFNjaWVudGlzdHMgaGF2ZSBncmVhdCBwb3dlciBhbmQgY3JlZGliaWxpdHksIGdpdmVuIG1hY2hpbmUgaW1wZXJmZWN0aW9ucywgaWYgdGhpcyBwb3dlciBpcyBub3QgdXNlZCByZXNwb25zaWJseSBpdCBjYW4gaGFybSBtYW55IHBlb3BsZS4gICAKCg==